﻿using PageTypeBuilder.Reflection;

namespace PageTypeBuilderUI.Plugins.Admin
{
    using System.Web.UI.HtmlControls;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using EPiServer;
    using EPiServer.DataAbstraction;
    using EPiServer.Security;
    using EPiServer.UI;
    using log4net;
    using PageTypeBuilder.Abstractions;
    using PageTypeBuilder.Discovery;
    using PageTypeBuilder.Synchronization;
    using EPiServer.PlugIn;
    using EPiServer.Core;

    [GuiPlugIn(Area = PlugInArea.AdminMenu, DisplayName = "Match properties with PageTypeBuilder",
        RequiredAccess = AccessLevel.Administer, Url = "~/util/PTBPropertyMatch.aspx")]
    public partial class PropertyMatch : SystemPageBase
    {
        
        #region Members

        private static readonly ILog Logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private int _count;
        private List<PageTypeDefinition> _pageTypes;

        #endregion Members

        #region Properties

        /// <summary>
        /// Gets the page types.
        /// </summary>
        /// <value>The page types.</value>
        private IEnumerable<PageTypeDefinition> PageTypes
        {
            get
            {
                return _pageTypes ?? (_pageTypes = (new PageTypeDefinitionLocator(new AppDomainAssemblyLocator())).GetPageTypeDefinitions().ToList());
            }
        }

        #endregion Properties

        #region Event handlers

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Page.PreInit"/> event at the beginning of page initialization.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);
            Page.MasterPageFile = EPiServer.Configuration.Settings.Instance.UIUrl + "MasterPages/EPiServerUI.Master";
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event to initialize the page.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            RepeaterPageTypes.ItemDataBound += RepeaterPageTypesItemDataBound;
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Load"/> event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!User.IsInRole("WebAdmins") && !User.IsInRole("Administrators"))
                Response.Redirect("/", true);

            SystemMessageContainer.Heading = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/name");
            SystemMessageContainer.Description = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/description");
            BulkRemovePropertiesButton.Enabled = true;
            BulkUpdatePropertiesButton.Enabled = true;
            ReBind();
        }

        #endregion Event handlers

        #region Helpers

        /// <summary>
        /// Rebinds the page controls.
        /// </summary>
        private void ReBind()
        {
            List<PageType> pageTypes = PageType.List().OrderBy(pageType => pageType.Name).ToList();
            RepeaterPageTypes.DataSource = pageTypes;
            RepeaterPageTypes.DataBind();
            DataBind();
        }

        protected void RemoveProperties(object sender, EventArgs e)
        {
            List<int> propertyIds = Request.Form.AllKeys
                .Where(key => key.Contains("RemoveProperty_"))
                .Select(key => int.Parse(key.Substring(key.LastIndexOf("_") + 1))).ToList();

            foreach (PageDefinition pageDefinition in propertyIds.Select(PageDefinition.Load))
                pageDefinition.Delete();

            ReBind();
        }

        protected void UpdateProperties(object sender, EventArgs e)
        {
            List<string> keys = Request.Form.AllKeys
                .Where(key => key.Contains("ChangeProperty_"))
                .Select(key => key.Substring(key.LastIndexOf("_") + 1)).ToList();

            foreach (string key in keys)
            {
                int pageTypeId = int.Parse(key.Substring(0, key.IndexOf(":")));
                int pageDefinitionId = int.Parse(key.Substring(key.IndexOf(":") + 1));

                PageType pageType = PageType.Load(pageTypeId);
                PageDefinition pageDefinition = PageDefinition.Load(pageDefinitionId);

                PageTypePropertyDefinitionLocator locator = new PageTypePropertyDefinitionLocator();
                PageTypeDefinition tempType = this.PageTypes.SingleOrDefault(p => string.Equals(p.GetPageTypeName(), pageType.Name));
                var properties = locator.GetPageTypePropertyDefinitions(new WrappedPageType(pageType), tempType.Type);

                var ptbProperty = properties.SingleOrDefault(p => p.Name == pageDefinition.Name);

                Type propertyType = ptbProperty.PageTypePropertyAttribute.Type ?? (new PageDefinitionTypeMapper(new PageDefinitionTypeFactory())).GetDefaultPropertyType(ptbProperty.PropertyType);

                if (propertyType == null || string.Equals(propertyType.Name, pageDefinition.Type.DefinitionType.Name))
                    continue;

                PageDefinitionType pageDefinitionType = PageDefinitionType.List().Where(current => current.DefinitionType != null && string.Equals(current.DefinitionType.Name, propertyType.Name)).FirstOrDefault();
                pageDefinition.Type = pageDefinitionType;

                if (pageDefinitionType == null) 
                    continue;

                if (pageDefinition.DefaultValueType == DefaultValueType.Value && !IsDefaultValueValid(pageDefinition))
                {
                    InvalidPropertyValueException exception = new InvalidPropertyValueException(pageDefinition.Type.LocalizedName, pageDefinition.DefaultValue);
                    CustomValidator validator = new CustomValidator();
                    validator.ID = Guid.NewGuid().ToString("N");
                    validator.IsValid = false;
                    string formatString = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/errorMessageFormatString");
                    validator.ErrorMessage = exception.Message + string.Format(formatString, pageType.Name, pageDefinition.Name);
                    Page.Validators.Add(validator);
                }
                else
                    pageDefinition.Save();
            }

            ReBind();
        }

        private bool IsDefaultValueValid(PageDefinition pageDefinition)
        {
            PropertyData propertyData;
            try
            {
                if (string.IsNullOrEmpty(pageDefinition.Type.TypeName))
                    propertyData = PropertyData.CreatePropertyDataObject(pageDefinition.Type.DataType);
                else
                    propertyData = PropertyData.CreatePropertyDataObject(pageDefinition.Type.AssemblyName, pageDefinition.Type.TypeName);

                propertyData.ParseToSelf(pageDefinition.DefaultValue);
            }
            catch
            {
                return false;
            }

            return propertyData.Value != null;
        }

        #endregion

        #region Events

        /// <summary>
        /// Handles the ItemDataBound event of the RepeaterPageTypes control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Web.UI.WebControls.RepeaterItemEventArgs"/> instance containing the event data.</param>
        void RepeaterPageTypesItemDataBound(object sender, RepeaterItemEventArgs e)
        {
            if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType != ListItemType.AlternatingItem)
                return;

            _count = 0;
            Repeater repeater = e.Item.FindControl("RepeaterProperties") as Repeater;
            PageType pagetype = e.Item.DataItem as PageType;

            if (repeater == null)
                return;

            repeater.ItemDataBound += RepItemDataBound;

            if (pagetype != null)
                repeater.DataSource = pagetype.Definitions;

            repeater.DataBind();
        }

        /// <summary>
        /// Reps the item data bound.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Web.UI.WebControls.RepeaterItemEventArgs"/> instance containing the event data.</param>
        void RepItemDataBound(object sender, RepeaterItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Footer)
            {
                if (_count == 0 && CheckBoxOnlyMissmatch.Checked)
                    ((Control)sender).Parent.Visible = false;

                return;
            }

            if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType != ListItemType.AlternatingItem)
                return;

            bool missmatch = false;
            string highlightClass = string.Empty;
            PageDefinition property = e.Item.DataItem as PageDefinition;

            if (property != null)
            {
                PageType pageType = PageType.Load(property.PageTypeID);
                PageTypePropertyDefinitionLocator locator = new PageTypePropertyDefinitionLocator();

                PageTypeDefinition tempType = PageTypes.SingleOrDefault(p => p.GetPageTypeName() == pageType.Name);

                if (tempType == null)
                {
                    Literal ltl = e.Item.FindControl("LiteralMissmatch") as Literal;

                    if (ltl != null)
                        ltl.Text = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/couldNotFindPropertyType");

                    return;
                }

                var props = locator.GetPageTypePropertyDefinitions(new WrappedPageType(pageType), tempType.Type);

                var ptbproperty = props.SingleOrDefault(p => p.Name == property.Name);

                if (ptbproperty == null)
                {
                    LinkButton remove = e.Item.FindControl("LinkButtonRemove") as LinkButton;

                    if (remove != null)
                    {
                        remove.Text = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/removeProperty"); ;
                        remove.CommandArgument = property.ID.ToString();
                        remove.Click += RemoveClick;
                    }

                    missmatch = true;
                    _count++;

                    Literal ltl = e.Item.FindControl("LiteralMissmatch") as Literal;

                    if (ltl != null)
                        ltl.Text = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/couldNotFindPageTypeBuilderProperty");

                    // add check box for bulk removal
                    CheckBox checkBox = e.Item.FindControl("RemovePropertyCheckBox") as CheckBox;
                    checkBox.Visible = true;
                    checkBox.ID = string.Format("RemoveProperty_{0}", property.ID);
                    checkBox.Attributes.Add("onclick", string.Format("javascript:UpdateSelectAllCheckBox('RemovePropertySelectAll', '{0}', 'RemoveProperty_');",
                        BulkRemovePropertiesButton.ClientID));
                    checkBox.Checked = Request.Form.AllKeys.Any(current => current.Contains(checkBox.ID));

                    if (checkBox.Checked)
                    {
                        highlightClass = "ptbDeleteRow";
                        //BulkRemovePropertiesButton.Enabled = true;
                    }
                }
                else
                {
                    Literal l = e.Item.FindControl("LiteralType") as Literal;

                    try
                    {
                        Type t = ptbproperty.PageTypePropertyAttribute.Type ?? (new PageDefinitionTypeMapper(new PageDefinitionTypeFactory())).GetDefaultPropertyType(ptbproperty.PropertyType);
                        string typename = (t != null ? t.Name : LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/couldNotGetType"));

                        if (l != null)
                            l.Text = typename;

                        if (typename != property.Type.DefinitionType.Name)
                        {
                            Literal ltl = e.Item.FindControl("LiteralMissmatch") as Literal;

                            if (ltl != null)
                                ltl.Text = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/typeMismatch"); ;

                            missmatch = true;
                            _count++;

                            // http://local.sn.se/EPiUi/CMS/Admin/EditPageTypeField.aspx?typeId=116
                            HyperLink link = e.Item.FindControl("HyperLinkAction") as HyperLink;

                            if (link != null)
                            {
                                link.Target = "_blank";
                                link.Text = LanguageHelper.Translate("/pageTypeBuilderPropertyMatch/editProperty");
                                link.NavigateUrl = UriSupport.ResolveUrlFromUIBySettings("Admin/") + "EditPageTypeField.aspx?typeId=" + property.ID;
                            }

                            // add check box for bulk type update
                            CheckBox checkBox = e.Item.FindControl("ChangePropertyCheckBox") as CheckBox;
                            checkBox.Visible = true;
                            checkBox.ID = string.Format("ChangeProperty_{0}:{1}", pageType.ID, property.ID);
                            checkBox.Attributes.Add("onclick", string.Format("javascript:UpdateSelectAllCheckBox('ChangePropertySelectAll', '{0}', 'ChangeProperty_');",
                                BulkUpdatePropertiesButton.ClientID));
                            checkBox.Checked = Request.Form.AllKeys.Any(current => current.Contains(checkBox.ID));

                            if (checkBox.Checked)
                            {
                                highlightClass = "ptbUpdateRow";
                                //BulkUpdatePropertiesButton.Enabled = true;
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        Logger.Error(exception);
                    }
                }
            }

            if (!string.IsNullOrEmpty(highlightClass))
            {
                HtmlTableRow tableRow = e.Item.FindControl("TableRow") as HtmlTableRow;
                tableRow.Attributes.Add("class", highlightClass);
            }

            if (CheckBoxOnlyMissmatch.Checked && !missmatch)
            {
                e.Item.Visible = false;
            }
        }

        /// <summary>
        /// Handles the Click event of the remove control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void RemoveClick(object sender, EventArgs e)
        {
            LinkButton btn = sender as LinkButton;

            if (btn == null)
                return;

            int id = int.Parse(btn.CommandArgument);
            PageDefinition pageDefinition = PageDefinition.Load(id);
            pageDefinition.Delete();
            ReBind();
        }

        #endregion

    }
}
